#include "Sock.hpp"
#include <sys/select.h>
#define NUM (sizeof(fd_set) * 8)
int fd_num[NUM];

void Usage(char *arg)
{
    std::cout << "Usage:" << arg << " port" << std::endl;
}

int main(int argc, char *argv[])
{
    if (argc != 2)
    {
        Usage(argv[0]);
    }

    uint16_t port = (uint16_t)atoi(argv[1]);
    int listen_sock = Sock::SOCKET();
    Sock::BIND(listen_sock, port);
    Sock::LISTEN(listen_sock);
    /*
    Here should not use `accept` directly,because the essence of what it rechives is that get link through the `listent_sock`.
    But we don't know if the link,which matches the `listen_sock`,is ready,in order to induce the account of time
    spend in `accept`,`read`,`write`,`recv` and `send` blocking wait,
    we should let `select` to do the thing of waiting and let them work directly with the data;
    */

    // cycle of events
    fd_set rfds;
    memset(fd_num, -1, sizeof(fd_num));
    fd_num[0] = listen_sock;
    for (;;)
    {
        FD_ZERO(&rfds);
        int max_fd = -1;
        for (unsigned int i = 0; i < NUM; i++)
        {
            if (fd_num[i] < 0)
            {
                continue;
            }
            FD_SET(fd_num[i], &rfds); // set all fds;
            if (max_fd < fd_num[i])
            {
                max_fd = fd_num[i];
            }
        }
        struct timeval timeout = {0, 0};                              // 5s
        int n = select(max_fd + 1, &rfds, nullptr, nullptr, nullptr); // temporarily blocked
        switch (n)
        {
        case -1:
            std::cerr << "select error" << std::endl;
            break;
        case 0:
            std::cerr << "select timeout" << std::endl;
            break;
        default:
            std::cout << "select ready" << std::endl;
            for (unsigned int i = 0; i < NUM; i++)
            {
                if (fd_num[i] < 0)
                {
                    continue;
                }

                // determine whether the value of rfds is valid;
                if (FD_ISSET(fd_num[i], &rfds))
                {

                    std::cout << "read event is ready" << std::endl;

                    // Here should determine whether the value of fd_num[i] is equal to listen_sock;
                    if (fd_num[i] == listen_sock)
                    {

                        std::cout << "listen_sock : " << listen_sock << ", new link is ready" << std::endl;
                        int sock = Sock::ACCEPT(listen_sock);

                        std::cout << "listen_sock : " << listen_sock << ", new link has got success" << std::endl;
                        // we got the `sock`,but that doesn't mean you can start using `read ,recv ...`
                        // beacuse we don't know if the data in this new `sock`  is ready;
                        // so we need continuly to use select to reduce the account of time spend in blocking wait;
                        unsigned int pos = 1;
                        for (; pos < NUM; pos++)
                        {
                            if (fd_num[pos] < 0)
                            {
                                break;
                            }
                        }

                        // if pos less than NUM ,indicate that this location is empty;
                        if (pos < NUM)
                        {
                            std::cout << "new sock : " << sock << ", has been added to fd_num" << std::endl;
                            fd_num[pos] = sock;
                        }
                        else
                        { // if pos greater than or equal to NUM,indicate that the server had be full;
                            close(sock);
                        }
                    }
                    else
                    {
                        // here is common sock;
                        // we can read context directly
                        // but it's not certain that we can read it all  at once
                        // so,for demonstration purposes,we assume that could read it all;

                        char recv_buffer[1024];
                        size_t s = recv(fd_num[i], recv_buffer, sizeof(recv_buffer) - 1, 0);
                        if (s > 0)
                        { // read success
                            recv_buffer[s] = 0;
                            std::cout << "client : " << fd_num[i] << ",recv messages:" << recv_buffer << std::endl;
                        }
                        else if (s == 0)
                        { // client has close
                            // server also close sock,and delete sock from fd_num;
                            close(fd_num[i]);
                            fd_num[i] = -1;
                            std::cout << "client has close and server also has delete sock from fd_num[i]" << std::endl;
                        }
                        else
                        {
                            // read error;
                        }
                    }
                }
            }
            break;
        }
    }
    return 0;
}